﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using Rsdn.Janus.Core.Extensibility;
using Rsdn.SmartApp;

namespace Rsdn.Janus
{
	using ProviderSvc = IRegElementsService<ExtensionInfoProviderInfo>;

	/// <summary>
	/// Реализация <see cref="IJanusExtensionManager"/>
	/// </summary>
	[Service(typeof (IJanusExtensionManager))]
	internal class JanusExtensionManager : IJanusExtensionManager
	{
        private const string _extensionDescriptorSchemaResource =
            "Rsdn.Janus.Core.Extensibility.JanusExtensionDescriptor.xsd";

        private const string _extensionDescriptorSchemaUri =
            "http://rsdn.ru/projects/Janus/JanusExtensionDescriptor.xsd";

        private readonly List<IExtensionInfoProvider> _infoProviders =
			new List<IExtensionInfoProvider>();

		public JanusExtensionManager(IServiceProvider serviceProvider)
		{
			var svc = serviceProvider.GetService<ProviderSvc>();
			if (svc != null)
				foreach (var info in svc.GetRegisteredElements())
					_infoProviders.Add(CreateProvider(serviceProvider, info.ProviderType));
		}


	    public static IExtensionInfoProvider CreateProvider(
			IServiceProvider serviceProvider,
			Type extensionType)
		{
			return (IExtensionInfoProvider)extensionType.CreateInstance(serviceProvider);
		}

		#region IJanusExtensionManager Members
		public IExtensionInfoProvider[] GetInfoProviders()
		{
			return _infoProviders.ToArray();
		}
		#endregion

        /// <summary>
        /// Gets the extension assemblies.
        /// </summary>
        /// <param name="ownerAssemblyType">Type of the owner assembly.</param>
        /// <param name="extDir">The ext dir.</param>
        /// <param name="loadAll">if set to <c>true</c> [load all].</param>
        /// <returns></returns>
        public static IEnumerable<ExtensionLoadInfo> GetExtensionAssemblies(Type ownerAssemblyType, string extDir, bool loadAll)
        {
            XmlReaderSettings readerSettings = GetXmlReaderSettings(ownerAssemblyType);

            foreach (string dir in Directory.GetDirectories(extDir))
            {
                string descFile = Path.Combine(dir, ExtensionDescriptorFileName);
                if (!File.Exists(descFile))
                {
                    continue;
                }
                XmlNodeList nodes = GetXmlNodes(descFile, readerSettings);
                if (nodes != null)
                {
                    foreach (XmlElement element in nodes)
                    {
                        string loadAttribute = GetLoadAttributeValue(element);
                        bool loadAllowed = IsLoadAllowed(loadAttribute);
                        if (loadAll || loadAllowed)
                        {
                            yield return new ExtensionLoadInfo(dir, element);
                            //yield return Path.Combine(dir, element.InnerText);
                        }
                    }
                }
            }
        }

	    public static bool IsLoadAllowed(string loadAttribute)
	    {
	        return string.IsNullOrEmpty(loadAttribute) || (loadAttribute == "yes");
	    }

	    internal static string GetLoadAttributeValue(XmlElement element)
	    {
	        return element.GetAttribute("load", string.Empty);
	    }

        internal static void SetLoadAttributeValue(XmlElement element, bool loadAllowed)
        {
            //if (string.IsNullOrEmpty(GetLoadAttributeValue(element)))
            element.SetAttribute("load", "", loadAllowed ? "yes" : "no");
        }

	    public static XmlNodeList GetXmlNodes(string descFile, XmlReaderSettings readerSettings)
	    {
	        XmlNodeList nodes;
            using (XmlReader reader = XmlReader.Create(descFile, readerSettings))
            {
                XmlDocument xDoc = new XmlDocument();
                XmlNamespaceManager nsMgr = new XmlNamespaceManager(xDoc.NameTable);
                nsMgr.AddNamespace("d", _extensionDescriptorSchemaUri);
                xDoc.Load(reader);
                nodes = xDoc.SelectNodes("/d:janus-extension-assemblies/d:extension-assembly", nsMgr);
            }

	        return nodes;
	    }

	    public static XmlReaderSettings GetXmlReaderSettings(Type ownerAssemblyType)
	    {
	        Stream stream = ownerAssemblyType.Assembly.GetRequiredResourceStream(_extensionDescriptorSchemaResource);
	        XmlSchema schema = XmlSchema.Read(stream,null);
	        XmlReaderSettings readerSettings = new XmlReaderSettings();

	        readerSettings.Schemas.Add(schema);
	        return readerSettings;
	    }

	    public static string ExtensionDescriptorFileName
        {
            get { return "Janus.Extension"; }
        }

	}
}
